/*
 * Copyright 2025, Amor. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *      Amor
 */

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include "runtime_loader_private.h"

#include <runtime_loader.h>

static status_t
relocate_rela(image_t *rootImage, image_t *image, Elf64_Rela *rel,
              size_t relLength, SymbolLookupCache *cache)
{
    for (size_t i = 0; i < relLength / sizeof(Elf64_Rela); i++)
    {
        int type = ELF64_R_TYPE(rel[i].r_info);
        int symIndex = ELF64_R_SYM(rel[i].r_info);
        Elf64_Addr symAddr = 0;
        image_t *symbolImage = NULL;

        // Resolve the symbol, if any.
        if (symIndex != 0)
        {
            Elf64_Sym *sym = SYMBOL(image, symIndex);

            status_t status = resolve_symbol(rootImage, image, sym, cache,
                                             &symAddr, &symbolImage);
            if (status != B_OK)
            {
                TRACE(("resolve symbol \"%s\" returned: %" B_PRId32 "\n",
                       SYMNAME(image, sym), status));
                printf("resolve symbol \"%s\" returned: %" B_PRId32 "\n",
                       SYMNAME(image, sym), status);
                return status;
            }
        }

        // Address of the relocation.
        Elf64_Addr relocAddr = image->regions[0].delta + rel[i].r_offset;

        // Calculate the relocation value.
        Elf64_Addr relocValue;
        switch (type)
        {
        case R_LARCH_NONE:
            continue;
        case R_LARCH_64:
        case R_LARCH_JUMP_SLOT:
            relocValue = symAddr + rel[i].r_addend;
            break;
        case R_LARCH_RELATIVE:
            relocValue = image->regions[0].delta + rel[i].r_addend;
            break;
        default:
            TRACE(("unhandled relocation type %d\n", type));
            return B_BAD_DATA;
        }

        *(Elf64_Addr *)relocAddr = relocValue;
    }

    return B_OK;
}

status_t
arch_relocate_image(image_t *rootImage, image_t *image, SymbolLookupCache *cache)
{
    status_t status;

    // Perform RELA relocations.
    if (image->rela)
    {
        status = relocate_rela(rootImage, image, image->rela, image->rela_len,
                               cache);
        if (status != B_OK)
            return status;
    }

    // PLT relocations (they are RELA on loongarch64).
    if (image->pltrel)
    {
        status = relocate_rela(rootImage, image, (Elf64_Rela *)image->pltrel,
                               image->pltrel_len, cache);
        if (status != B_OK)
            return status;
    }

    return B_OK;
}

status_t
arch_relocate_image(image_t *rootImage, image_t *image, Elf64_Rel *rel, int relLength)
{
    // LoongArch64使用RELA而不是REL类型的重定位
    return B_OK;
}

status_t
arch_relocate_image(image_t *rootImage, image_t *image, Elf64_Rela *rel, int relLength)
{
    // 这个函数已经被上面的arch_relocate_image(image_t*, image_t*, SymbolLookupCache*)替代
    return B_OK;
}